﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Authorization;
using Microsoft.Extensions.Logging;
using EcoSystem.Office.Excel;
using EcoSystem.Office.Enums;

namespace EcoSystem
{
    /// <summary>
    /// 报表配置接口实现
    /// </summary>
    public class HttpApiConfigAppService : CrudAppService<
         HttpApiConfig,
         HttpApiConfigDto,
         Guid,
         GetHttpApiConfigInput,
         CreateOrUpdateHttpApiConfigDto,
         CreateOrUpdateHttpApiConfigDto>, IHttpApiConfigAppService
    {
        private readonly IScriptAppService _scriptAppService;
        public IPermissionChecker PermissionChecker { get; private set; }

        public HttpApiConfigAppService(
            IRepository<HttpApiConfig, Guid> repository,
            IScriptAppService scriptAppService)
            : base(repository)
        {
            _scriptAppService = scriptAppService;
        }


        public override async Task<HttpApiConfigDto> GetAsync(Guid id)
        {
            var result = await (await Repository.GetQueryableAsync()).FirstOrDefaultAsync(x => x.Id == id);
            return MapToGetOutputDto(result);
        }

        /// <summary>
        /// 创建报表配置
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public override async Task<HttpApiConfigDto> CreateAsync(CreateOrUpdateHttpApiConfigDto input)
        {
            if (string.IsNullOrEmpty(input.Name))
            {
                throw new UserFriendlyException("名称不能为空!");
            }
            if (string.IsNullOrEmpty(input.Code))
            {
                throw new UserFriendlyException("编码不能为空!");
            }
            if (await (await Repository.GetQueryableAsync()).AnyAsync(x => x.Code == input.Code))
            {
                throw new UserFriendlyException("API接口编码重复，指定的编码在系统中已存在!");
            }
            var result = await base.CreateAsync(input);
            RegisterScriptMethod(result);
            return result;
        }

        /// <summary>
        /// 更新报表配置
        /// </summary>
        /// <param name="id"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public override async Task<HttpApiConfigDto> UpdateAsync(Guid id, CreateOrUpdateHttpApiConfigDto input)
        {
            var report = await (await Repository.GetQueryableAsync()).FirstOrDefaultAsync(x => x.Id == id);
            if (report == null)
            {
                throw new UserFriendlyException("当前接口不存在");
            }
            if (string.IsNullOrEmpty(input.Name))
            {
                throw new UserFriendlyException("名称不能为空!");

            }
            if (string.IsNullOrEmpty(input.Code))
            {
                throw new UserFriendlyException("编码不能为空!");
            }
            if (await (await Repository.GetQueryableAsync()).AnyAsync(x => x.Code == input.Code && x.Id != id))
            {
                throw new UserFriendlyException("API接口编码重复，指定的编码在系统中已存在!");
            }
            var result = await base.UpdateAsync(id, input);
            RegisterScriptMethod(result);
            return result;
        }

        /// <summary>
        /// 获取列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async override Task<PagedResultDto<HttpApiConfigDto>> GetListAsync(GetHttpApiConfigInput input)
        {
            var query = (await Repository.GetQueryableAsync()).AsNoTracking();
            query = query.WhereIf(input.CategoryId.HasValue, x => x.CategoryId == input.CategoryId)
                .WhereIf(!string.IsNullOrWhiteSpace(input.Name), x => x.Name.Contains(input.Name) || x.Code.Contains(input.Name))
                .WhereIf(!string.IsNullOrWhiteSpace(input.Code), x => x.Code.Contains(input.Code))
                .WhereIf(input.Status.HasValue, x => x.Status == input.Status);
            var totalCount = await query.CountAsync();
            var list = await query.OrderByDescending(x => x.CreationTime).PageBy(input.SkipCount, input.MaxResultCount).ToListAsync();
            return new PagedResultDto<HttpApiConfigDto>()
            {
                TotalCount = totalCount,
                Items = ObjectMapper.Map<List<HttpApiConfig>, List<HttpApiConfigDto>>(list)
            };
        }

        public async Task<ListResultDto<HttpApiConfigDto>> GetAllAsync(GetHttpApiConfigInput input)
        {
            var query = (await Repository.GetQueryableAsync()).AsNoTracking();
            query = query.WhereIf(input.CategoryId.HasValue, x => x.CategoryId == input.CategoryId)
                .WhereIf(!string.IsNullOrWhiteSpace(input.Name), x => x.Name.Contains(input.Name) || x.Code.Contains(input.Name))
                .WhereIf(!string.IsNullOrWhiteSpace(input.Code), x => x.Code.Contains(input.Code))
                .WhereIf(input.Status.HasValue, x => x.Status == input.Status);
            var list = await query.ToListAsync();
            var items = ObjectMapper.Map<List<HttpApiConfig>, List<HttpApiConfigDto>>(list);
            return new ListResultDto<HttpApiConfigDto>() { Items = items };
        }

        public async Task<ListResultDto<HttpApiConfigDto>> GetOwnsListAsync(bool onlyEnabled = true)
        {
            var query = (await Repository.GetQueryableAsync()).AsNoTracking();
            query = query.WhereIf(onlyEnabled, x => x.Status == onlyEnabled);
            var list = await query.ToListAsync();
            var items = ObjectMapper.Map<List<HttpApiConfig>, List<HttpApiConfigDto>>(list);
            return new ListResultDto<HttpApiConfigDto>() { Items = items };
        }

        public async Task<HttpApiConfigDto> GetForCodeAsync(string code)
        {
            var result = await (await Repository.GetQueryableAsync()).FirstOrDefaultAsync(x => x.Code == code);
            return MapToGetOutputDto(result);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="code"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<object> QueryDataAsync(string code, DictionaryRequestInputDto input)
        {

            var config = await GetForCodeAsync(code);
            if (config == null)
            {
                throw new UserFriendlyException("接口不存在");
            }
            if (!config.Status)
            {
                throw new UserFriendlyException("当前接口已停用");
            }
            if (!CurrentUser.IsAuthenticated && config.AllowAnonymous != true)
            {
                throw new AbpAuthorizationException("没有接口调用权限!");
            }
            if (!string.IsNullOrWhiteSpace(config.PermissionName))
            {
                if (!await PermissionChecker.IsGrantedAsync(config.PermissionName))
                {
                    throw new AbpAuthorizationException("没有接口调用权限!");
                }
            }
            return await ExecuteScriptQueryAsync(config, input);
        }

        /// <summary>
        /// 注册脚本函数
        /// </summary>
        /// <param name="item"></param>
        private void RegisterScriptMethod(HttpApiConfigDto item)
        {
            string methodName = $"$method_httpapi_{item.Code}";
            var content = item.ScriptContent ?? "return '';";
            _scriptAppService.RegisterMethod(methodName, content, new string[] { "$params" });
        }


        /// <summary>
        /// 执行脚本查询
        /// </summary>
        /// <param name="config"></param>
        /// <param name="input"></param>
        /// <param name="isTest"></param>
        /// <returns></returns>
        private async Task<object> ExecuteScriptQueryAsync(HttpApiConfigDto config, DictionaryRequestInputDto input, bool isTest = false)
        {
            var data = new Dictionary<string, object>();
            data.Add("$params", input);
            if (isTest)
            {
                string methodName = $"$method_test_httpapi_{config.Code}";
                string content = config.ScriptContent ?? "return '';";
                _scriptAppService.RegisterMethod(methodName, content, new string[] { "$params" });
                var result = _scriptAppService.ExecuteMethod<object>(methodName, data);
                return await Task.FromResult(result);
            }
            else
            {
                try
                {
                    string methodName = $"$method_httpapi_{config.Code}";
                    var result = _scriptAppService.ExecuteMethod<object>(methodName, data);
                    return await Task.FromResult(result);
                }
                catch (Exception ex)
                {
                    var ex2 = ex.InnerException ?? ex;
                    Logger.LogError(ex2, ex2.Message);
                    throw ex2;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="code"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<object> ExportDataAsync(string code, ExportExcelInputDto input)
        {
            var data = await QueryDataAsync(code, input.QueryParamaters);
            var items = GetExportRows(data, input);
            var options = CreateExportOptions(input.Options);
            var stream = ExcelExportService.WriteExcelToStream(items as List<Dictionary<string, object>>, options, ExcelFileType.Excel2007);
            return stream.ToArray();
        }

        private ExcelExportOptions<Dictionary<string, object>> CreateExportOptions(ExportExcelOption opts)
        {
            //导出操作
            var options = new ExcelExportOptions<Dictionary<string, object>>()
            {
                Caption = opts.Caption,
                DrawCellBorder = opts.AutoSizeColumn,
                HeaderRows = opts.HeaderRows,
                Columns = opts.Columns,
                HideColumnTitleRow = opts.HideColumnTitleRow,
                FooterContent = opts.FooterContent,
                AutoSizeColumn = opts.AutoSizeColumn,
            };
            if (!opts.SheetName.IsNullOrWhiteSpace())
            {
                options.SheetName = opts.SheetName;
            }
            if (!opts.DateTimeFormatter.IsNullOrWhiteSpace())
            {
                options.DateTimeFormatter = opts.DateTimeFormatter;
            }
            if (opts.RowHeight > 0)
            {
                options.RowHeight = opts.RowHeight;
            }
            return options;
        }

        /// <summary>
        /// 获取要导出的数据行
        /// </summary>
        /// <param name="data"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        private List<Dictionary<string, object>> GetExportRows(object data, ExportExcelInputDto input)
        {
            var type = data.GetType();
            var listType = typeof(List<>);
            var dictType = typeof(Dictionary<,>);
            if (type.IsGenericType && type.GetGenericTypeDefinition() == dictType)
            {
                var dict = data as Dictionary<string, object>;
                var itemKey = string.IsNullOrEmpty(input.ItemsKey) ? "Items" : input.ItemsKey;
                if (dict[itemKey] == null)
                {
                    throw new UserFriendlyException("导出失败，返回数据结构未找到要导出的数据项，请检查请求参数是否正确");
                }
                var list = dict[itemKey] as List<Dictionary<string, object>>;
                if (!input.SummaryKey.IsNullOrWhiteSpace())
                {
                    if (dict[input.SummaryKey] != null)
                    {
                        var summaryItem = dict[input.SummaryKey] as Dictionary<string, object>;
                        list.Add(summaryItem);
                    }
                }
                return list;
            }
            else if (type.IsGenericType && type.GetGenericTypeDefinition() == listType)
            {
                return data as List<Dictionary<string, object>>;
            }
            else
            {
                throw new UserFriendlyException("导出失败，返回数据结构未找到要导出的数据项，请检查请求参数是否正确");
            }
        }
    }
}
